#include "down_and_up.h"

#define save_path "server_storage/" 
// 客户端上传文件（支持断点续传）
int putsfunc(int sockfd, const char *filename) {
    char buf[100]={0};
    strcpy(buf,save_path);
    strcat(buf,filename);
    // 1. 计算文件哈希
    char file_hash[65];
    sha256_file(buf, file_hash);
    printf("[Client] File hash: %s\n", file_hash);

    // 2. 发送哈希给服务端检查是否已存在
    trains_t packet;
    strncpy(packet.hash, file_hash, sizeof(packet.hash));
    send(sockfd, &packet, sizeof(packet), 0);
    // 3. 获取本地文件大小
    struct stat st;
    stat(buf, &st);
    off_t file_size = st.st_size;

    // 4. 发送文件名和大小
    strncpy(packet.data, filename, sizeof(packet.data));
    packet.length = sizeof(file_size);
    memcpy(packet.data + strlen(filename) + 1, &file_size, sizeof(file_size));
    send(sockfd, &packet, sizeof(packet), MSG_NOSIGNAL);

    // 5. 接收服务端响应（是否需上传）
    int need_upload;
    recv(sockfd, &need_upload, sizeof(need_upload),MSG_WAITALL);
    if (need_upload) {
        printf("[Client] File already exists on server (skipping upload)\n");
        return 0;
    }

    // 6. 断点续传：获取已上传的偏移量
    off_t offset;
    recv(sockfd, &offset, sizeof(offset), MSG_WAITALL);
    printf("[Client] Resuming upload from offset: %ld\n", offset);

    // 7. 分块上传文件
    int fd = open(buf, O_RDONLY);
    lseek(fd, offset, SEEK_SET);  // 跳转到断点位置
        printf("file_size=%ld\n",file_size);
    while (offset < file_size) {
        ssize_t bytes_read = read(fd, packet.data, sizeof(packet.data));
        ERROR_CHECK(bytes_read,-1,"read");
        packet.length = bytes_read;
        send(sockfd, &packet, sizeof(packet), 0);
        offset += bytes_read;
<<<<<<< HEAD
        printf("offset=%ld,bytes_read=%ld\r",offset,bytes_read);
=======
>>>>>>> origin/master
    }
    printf("\n");

    // 8. 发送结束标志
    packet.length = 0;
    send(sockfd, &packet, sizeof(packet), 0);
    close(fd);
    return 0;
}





// 客户端从服务端下载文件
int getsfunc(int sockfd, const char *filename) {
    trains_t packet;

    // 1. 发送下载请求（文件名）
    strncpy(packet.data, filename, sizeof(packet.data));
    packet.length = strlen(filename);
    send(sockfd, &packet, sizeof(packet), 0);

    // 2. 接收文件大小和哈希（服务端先校验是否存在）
    off_t file_size;
    recv(sockfd,&file_size,sizeof(file_size),MSG_WAITALL);
    recv(sockfd, &packet, sizeof(packet), MSG_WAITALL);
    if (packet.length == -1) {
        printf("[Client] Error: File not found on server.\n");
        return -1;
    }

    char file_hash[512]={0};
    memcpy(file_hash,packet.hash,strlen(packet.hash));
    printf("[Client] Downloading file: %s (Size: %ld, Hash: %s)\n", filename, file_size, file_hash);

    // 3. 断点续传：检查本地已下载部分
    off_t offset = 0;
    struct stat st;
    char buf[100]={0};
    strcpy(buf,save_path);
    strcat(buf,filename);

    if (stat(buf, &st) == 0) {
        offset = st.st_size;
        printf("[Client] Resuming download from offset: %ld\n", offset);
    }
    send(sockfd, &offset, sizeof(offset), 0);

    // 4. 接收文件数据
    int fd = open(buf, O_WRONLY | O_CREAT | (offset ? O_APPEND : O_TRUNC), 0666);
    lseek(fd, offset, SEEK_SET);

    while (offset < file_size) {
        recv(sockfd, &packet, sizeof(packet), MSG_WAITALL);
        if (packet.length <= 0) break;
        write(fd, packet.data, packet.length);
        offset += packet.length;
        printf("\rProgress: %.2f%%", (float)offset / file_size * 100);
        fflush(stdout);
    }
    close(fd);
    printf("\n[Client] Download complete.\n");

    // 5. 校验文件哈希
    char local_hash[512];
    sha256_file(buf, local_hash);
    if (strcmp(local_hash, file_hash) != 0) {
        printf("[Client] Error: File corrupted (hash mismatch)!\n");
        unlink(save_path);
        return -1;
    }
    return 0;
}


void sha256_file(const char *filename, char *output_hash) {
    FILE *fp = fopen(filename, "rb");
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX ctx;
    SHA256_Init(&ctx);

    char buffer[1024];
    size_t bytes;
    while ((bytes = fread(buffer, 1, sizeof(buffer), fp))) {
        SHA256_Update(&ctx, buffer, bytes);
    }
    SHA256_Final(hash, &ctx);
    fclose(fp);

    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
        sprintf(output_hash + (i * 2), "%02x", hash[i]);
    }
    output_hash[64] = '\0';
}

